/*
 * Hx chip interrupt controller
 *
 * Copyright (C) 2020 Corellium LLC
 * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 */

#define  __SYLIXOS_KERNEL
#include <SylixOS.h>
#include <linux/compat.h>

#define REG_ID_REVISION                 0x0000
#define REG_ID_CONFIG                   0x0004
#define REG_GLOBAL_CFG                  0x0010
#define REG_TIME_LO                     0x0020
#define REG_TIME_HI                     0x0028
#define REG_ID_CPUID                    0x2000
#define REG_IRQ_ACK                     0x2004
#define  REG_IRQ_ACK_TYPE_MASK          (15 << 16)
#define   REG_IRQ_ACK_TYPE_NONE         (0 << 16)
#define   REG_IRQ_ACK_TYPE_IRQ          (1 << 16)
#define   REG_IRQ_ACK_TYPE_IPI          (4 << 16)
#define    REG_IRQ_ACK_IPI_OTHER        0x40001
#define    REG_IRQ_ACK_IPI_SELF         0x40002
#define  REG_IRQ_ACK_NUM_MASK           (4095)
#define REG_IPI_SET                     0x2008
#define   REG_IPI_FLAG_SELF             (1 << 31)
#define   REG_IPI_FLAG_OTHER            (1 << 0)
#define REG_IPI_CLEAR                   0x200C
#define REG_IPI_DISABLE                 0x2024
#define REG_IPI_ENABLE                  0x2028
#define REG_IPI_DEFER_SET               0x202C
#define REG_IPI_DEFER_CLEAR             0x2030
#define REG_TSTAMP_CTRL                 0x2040
#define REG_TSTAMP_LO                   0x2048
#define REG_TSTAMP_HI                   0x204C
#define REG_IRQ_AFFINITY(i)             (0x3000 + ((i) << 2))
#define REG_IRQ_DISABLE(i)              (0x4100 + (((i) >> 5) << 2))
#define  REG_IRQ_xABLE_MASK(i)          (1 << ((i) & 31))
#define REG_IRQ_ENABLE(i)               (0x4180 + (((i) >> 5) << 2))
#define REG_CPU_REGION                  0x5000
#define  REG_CPU_LOCAL                  0x2000
#define  REG_CPU_SHIFT                  7
#define  REG_PERCPU(r,c)                ((r)+REG_CPU_REGION-REG_CPU_LOCAL+((c)<<REG_CPU_SHIFT))

#define HX_AIC_REG_BASE                 (0x20e100000)

static struct aic_chip_data {
    void *base;
    unsigned int num_irqs;
} aic;

void hx_aic_irq_disable(unsigned int vector)
{
    writel(REG_IRQ_xABLE_MASK(vector), aic.base + REG_IRQ_DISABLE(vector));
}

void hx_aic_irq_enable(unsigned int vector)
{
    writel(REG_IRQ_xABLE_MASK(vector), aic.base + REG_IRQ_ENABLE(vector));
}

BOOL hx_aic_irq_is_enable(unsigned int vector)
{
    BOOL is_enable = FALSE;

    is_enable = readl(aic.base + REG_IRQ_ENABLE(vector)) & REG_IRQ_xABLE_MASK(vector);

    return is_enable;
}

BOOL hx_aic_int_is_fiq(void)
{
    uint32_t ack;
    uint32_t type;

    ack = readl(aic.base + REG_IRQ_ACK);
    type = ack & REG_IRQ_ACK_TYPE_MASK;

    if ((type == REG_IRQ_ACK_TYPE_IRQ) ||
        (type == REG_IRQ_ACK_TYPE_IPI)) {
        return FALSE;
    } else {
        return TRUE;
    }
}

unsigned int hx_aic_get_vector(void)
{
    uint32_t ack;

    ack = readl(aic.base + REG_IRQ_ACK);

    return (ack & REG_IRQ_ACK_NUM_MASK);
}

static void hx_aic_cpu_prepare(unsigned int cpu)
{
    unsigned i;

    writel(REG_IPI_FLAG_SELF | REG_IPI_FLAG_OTHER, aic.base + REG_PERCPU(REG_IPI_ENABLE, cpu));

    for(i=0; i<aic.num_irqs; i++)
        writel(readl(aic.base + REG_IRQ_AFFINITY(i)) | (1u << cpu), aic.base + REG_IRQ_AFFINITY(i));
}

int hx_aic_init(void)
{
    unsigned i;

    aic.base = (void *)HX_AIC_REG_BASE;
    aic.num_irqs = readl(aic.base + REG_ID_CONFIG) & 0xFFFF;
    printk("Hx AIC: %d IRQs + 1 FIQ + 1 dummy\n", aic.num_irqs);

    for(i=0; i<aic.num_irqs; i++)
        writel(1, aic.base + REG_IRQ_AFFINITY(i));
    for(i=0; i<aic.num_irqs; i+=32)
        writel(-1u, aic.base + REG_IRQ_DISABLE(i));
    writel((readl(aic.base + REG_GLOBAL_CFG) & ~0xF00000) | 0x700000, aic.base + REG_GLOBAL_CFG);

    hx_aic_cpu_prepare(0);

    return 0;
}

